#include "fix_sche.h"
#include "task.h"
#include "array.h"
#include "worker_mgr.h"
#include "worker.h"
#include "sche_mgr.h"
#include "sche.h"

#include <malloc.h>
#include <assert.h>
#include <stdio.h>

#ifdef __linux__
#include <unistd.h>
#elif _WIN32
#include <process.h>
#else
#endif


C_CODE_BEGIN
struct fix_sche {
	// the array contains the specific data structure to handle the worker as well as perform the
	// schedule algorithm.
	struct array mwkr;
	// the initial number of worker
	size_t isize;
};

#define SCHE(fs) ( (struct sche*)((char*)(fs) - sizeof(struct sche)) )
#define FSCHE(s) ( (struct fix_sche*)((char*)(s) + sizeof(struct sche)) )

static const char* _worker_name( size_t uid ) {
	static char buf[256];
	sprintf( buf , "dbworker:%d" , uid );
	return buf;
}
 
/*
 * This operation should be very fast and light-weight
 */

static void _lp_find_light_worker( void* obj , int idx, void* others ) {
	int * res = (int*)others;
	struct worker* wk= *(struct worker**)(obj);
	size_t ld = wk->get_workload(wk);
	/* You can not cast *res since it is possible to eaqul -1 */
	if ( (int)(ld) < *res || *res == -1 ) {
		*res = (int)(ld);
		res[1] = idx;
	} 
}

static int _schedule ( struct fix_sche * fs ) {
	struct array* warr = &(fs->mwkr);
	int res[2];
	res[0]=-1;
	res[1]=-1;
	array_foreach( warr , _lp_find_light_worker , res );
	return res[1];
}
 
static void _fix_sche_dispatch( struct sche* f , const struct task* t ) {
	struct fix_sche * fs = FSCHE(f);
	 int idx = _schedule(fs);
	 struct worker* w = *(struct worker**)( array_index_at(&(fs->mwkr),idx) );
	 w->dispatch( w , t );
 }
 
 
struct quit {
   bool rn;
   const struct th_join_sec* ti;
};

 static void _lp_join_close( void* obj , int idx , void* others ) {
	 const struct quit * qt = (const struct quit *)(others);
	 bool res;
	 struct worker* w = *(struct worker**)(obj);
	 // signal quit operation
	 w->signal_quit(w,qt->rn);
	 // the worker will automatically release itself
	 // this must succeeded, otherwise the resource gonna leak.
	 res = w->join(w,qt->ti);
	 assert( res == true );
	 
 }
 
static void _fix_sche_quit( struct sche* f , bool right_now , const struct th_join_sec* ti ) {
	struct fix_sche* fs = FSCHE(f);
	struct quit qt;
	qt.ti = ti;
	qt.rn = right_now;
	array_foreach( &(fs->mwkr) , _lp_join_close , &qt );
	array_destroy( &(fs->mwkr) );
	free(f);
}

static size_t _fix_sche_get_worker_size( struct sche* f ) {
	return array_get_usesize( &(((struct fix_sche*)(f))->mwkr) );
}

static struct sche* _fix_sche_create( size_t size , struct worker_mgr* mgr ,const char* type ){
	struct sche* s = malloc( sizeof( struct fix_sche ) + sizeof(struct sche) );
	struct fix_sche* fs = FSCHE(s);
	size_t i = 0 ;
	struct worker* wk;
	fs->isize = size;
	array_create( &(fs->mwkr) , size , sizeof(struct worker*) );

	for( ; i < size ; ++i ) {
		wk = worker_mgr_spawn_worker( mgr , type , _worker_name(i) , false );
		assert(wk);
		array_push_back( &(fs->mwkr) , &wk  );
	}
	s->dispatch = _fix_sche_dispatch;
	s->get_worker_size = _fix_sche_get_worker_size;
	s->signal_quit = _fix_sche_quit;
	return s;
}


void _fix_sche_register( struct sche_mgr * mgr ) {
	sche_mgr_register(mgr,FIXSCHE_NAME,_fix_sche_create);
}
 
C_CODE_END
 